package com.thinkcar.core.utils;

import com.thinkcar.core.exception.extension.RestTemplateException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import java.util.Collections;
import java.util.Map;

import static com.thinkcar.core.enums.PublicExceptionEnum.*;

/**
 * @author: jiangyong
 * @date: 2025/10/18
 * @description: RestTemplateUtil
 */
@Component
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnBean(RestTemplate.class)
public class RestTemplateUtil {

    private static final Logger logger = LoggerFactory.getLogger(RestTemplateUtil.class);

    private final RestTemplate restTemplate;

    // 最大重试次数
    private static final int MAX_RETRY_COUNT = 3;
    // 重试间隔时间（毫秒）
    private static final long RETRY_INTERVAL = 1000L;

    @Autowired
    public RestTemplateUtil(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    /**
     * GET 请求
     *
     * @param url          请求地址
     * @param responseType 响应类型
     * @param <T>          响应类型泛型
     * @return 响应结果
     */
    public <T> T get(String url, Class<T> responseType) {
        return get(url, null, responseType);
    }

    /**
     * GET 请求（带请求头）
     *
     * @param url          请求地址
     * @param headers      请求头
     * @param responseType 响应类型
     * @param <T>          响应类型泛型
     * @return 响应结果
     */
    public <T> T get(String url, HttpHeaders headers, Class<T> responseType) {
        return get(url, headers, null, responseType);
    }

    /**
     * GET 请求（带请求头和参数）
     *
     * @param url          请求地址
     * @param headers      请求头
     * @param uriVariables URI参数
     * @param responseType 响应类型
     * @param <T>          响应类型泛型
     * @return 响应结果
     */
    public <T> T get(String url, HttpHeaders headers, Map<String, Object> uriVariables, Class<T> responseType) {
        return executeWithRetry(() -> {
            try {
                HttpEntity<String> entity = new HttpEntity<>(headers);
                ResponseEntity<T> response;

                if (uriVariables != null && !uriVariables.isEmpty()) {
                    response = restTemplate.exchange(url, HttpMethod.GET, entity, responseType, uriVariables);
                } else {
                    response = restTemplate.exchange(url, HttpMethod.GET, entity, responseType);
                }

                return handleResponse(response);
            } catch (RestClientException e) {
                logger.error("GET请求失败 - URL: {}, 错误: {}", url, e.getMessage());
                throw new RestTemplateException(RESTTEMPLATE_GET_FAIL, url, e);
            }
        }, url, "GET");
    }

    /**
     * POST 请求
     *
     * @param url          请求地址
     * @param requestBody  请求体
     * @param responseType 响应类型
     * @param <T>          响应类型泛型
     * @return 响应结果
     */
    public <T> T post(String url, Object requestBody, Class<T> responseType) {
        return post(url, null, requestBody, responseType);
    }

    /**
     * POST 请求（带请求头）
     *
     * @param url          请求地址
     * @param headers      请求头
     * @param requestBody  请求体
     * @param responseType 响应类型
     * @param <T>          响应类型泛型
     * @return 响应结果
     */
    public <T> T post(String url, HttpHeaders headers, Object requestBody, Class<T> responseType) {
        try {
            HttpEntity<Object> entity = new HttpEntity<>(requestBody, headers);
            ResponseEntity<T> response = restTemplate.exchange(url, HttpMethod.POST, entity, responseType);
            return handleResponse(response);
        } catch (RestClientException e) {
            logger.error("POST请求失败 - URL: {}, 错误: {}", url, e.getMessage());
            throw new RestTemplateException(RESTTEMPLATE_POST_FAIL, url, e);
        }
    }

    /**
     * POST 请求（表单提交）
     *
     * @param url          请求地址
     * @param formData     表单数据
     * @param responseType 响应类型
     * @param <T>          响应类型泛型
     * @return 响应结果
     */
    public <T> T postForm(String url, Map<String, String> formData, Class<T> responseType) {
        try {
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

            // 构建表单数据
            StringBuilder formBody = new StringBuilder();
            for (Map.Entry<String, String> entry : formData.entrySet()) {
                if (formBody.length() > 0) {
                    formBody.append("&");
                }
                formBody.append(entry.getKey()).append("=").append(entry.getValue());
            }

            HttpEntity<String> entity = new HttpEntity<>(formBody.toString(), headers);
            ResponseEntity<T> response = restTemplate.exchange(url, HttpMethod.POST, entity, responseType);
            return handleResponse(response);
        } catch (RestClientException e) {
            logger.error("POST表单请求失败 - URL: {}, 错误: {}", url, e.getMessage());
            throw new RestTemplateException(RESTTEMPLATE_POST_FORM_FAIL, url, e);
        }
    }

    /**
     * 带重试机制的请求执行
     *
     * @param request 请求执行函数
     * @param url     请求URL
     * @param method  请求方法
     * @param <T>     响应类型
     * @return 响应结果
     */
    private <T> T executeWithRetry(RequestFunction<T> request, String url, String method) {
        int retryCount = 0;
        RestClientException lastException = null;

        while (retryCount < MAX_RETRY_COUNT) {
            try {
                return request.execute();
            } catch (ResourceAccessException e) {
                // 网络连接异常，进行重试
                lastException = e;
                retryCount++;
                logger.warn("{}请求重试 {}/{} - URL: {}, 错误: {}", method, retryCount, MAX_RETRY_COUNT, url, e.getMessage());
                if (retryCount < MAX_RETRY_COUNT) {
                    try {
                        Thread.sleep(RETRY_INTERVAL);
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        throw new RestTemplateException(RESTTEMPLATE_REQUEST_INTERRUPTED, ie);
                    }
                }
            } catch (RestClientException e) {
                // 其他RestClient异常，不重试
                throw new RestTemplateException(RESTTEMPLATE_EXCEPTION, method, e);
            }
        }
        // 重试次数用尽后抛出异常
        logger.error("{}请求重试次数用尽 - URL: {}", method, url);
        throw new RestTemplateException(RESTTEMPLATE_RETRY_EXHAUSTED, method, lastException);
    }

    /**
     * 处理响应
     *
     * @param response ResponseEntity
     * @param <T>      响应类型
     * @return 响应体
     */
    private <T> T handleResponse(ResponseEntity<T> response) {
        if (response.getStatusCode().is2xxSuccessful()) {
            return response.getBody();
        } else {
            logger.error("HTTP错误状态码: {}", response.getStatusCode());
            throw new RestTemplateException(RESTTEMPLATE_RESPONSE_ERROR, response.getStatusCode());
        }
    }

    /**
     * 创建默认请求头（JSON格式）
     *
     * @return HttpHeaders
     */
    public HttpHeaders createJsonHeaders() {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
        return headers;
    }

    /**
     * 创建带认证的请求头
     *
     * @param token 认证token
     * @return HttpHeaders
     */
    public HttpHeaders createAuthHeaders(String token) {
        HttpHeaders headers = createJsonHeaders();
        headers.set("Authorization", "Bearer " + token);
        return headers;
    }

    /**
     * 请求执行函数接口
     */
    @FunctionalInterface
    private interface RequestFunction<T> {
        T execute();
    }
}